home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / math / ast53src.zip / GENERAL.C < prev    next >
C/C++ Source or Header  |  1996-09-29  |  29KB  |  1,092 lines

  1. /*
  2. ** Astrolog (Version 5.30) File: general.c
  3. **
  4. ** IMPORTANT NOTICE: The graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1996 by Walter D. Pullen
  6. ** (Astara@msn.com, http://www.magitech.com/~cruiser1/astrolog.htm).
  7. ** Permission is granted to freely use and distribute these routines
  8. ** provided one doesn't sell, restrict, or profit from them in any way.
  9. ** Modification is allowed provided these notices remain with any
  10. ** altered or edited versions of the program.
  11. **
  12. ** The main planetary calculation routines used in this program have
  13. ** been Copyrighted and the core of this program is basically a
  14. ** conversion to C of the routines created by James Neely as listed in
  15. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  16. ** available from Matrix Software. The copyright gives us permission to
  17. ** use the routines for personal use but not to sell them or profit from
  18. ** them in any way.
  19. **
  20. ** The PostScript code within the core graphics routines are programmed
  21. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  22. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  23. **
  24. ** The extended accurate ephemeris databases and formulas are from the
  25. ** calculation routines in the program "Placalc" and are programmed and
  26. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  27. ** (alois@azur.ch). The use of that source code is subject to
  28. ** regulations made by Astrodienst Zurich, and the code is not in the
  29. ** public domain. This copyright notice must not be changed or removed
  30. ** by any user of this program.
  31. **
  32. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  33. ** X Window graphics initially programmed 10/23-29/1991.
  34. ** PostScript graphics initially programmed 11/29-30/1992.
  35. ** Last code change made 9/22/1996.
  36. */
  37.  
  38. #include "astrolog.h"
  39.  
  40.  
  41. /*
  42. ******************************************************************************
  43. ** General Procedures.
  44. ******************************************************************************
  45. */
  46.  
  47. /* Swap two floating point values. */
  48.  
  49. void SwapR(d1, d2)
  50. real *d1, *d2;
  51. {
  52.   real temp;
  53.  
  54.   temp = *d1; *d1 = *d2; *d2 = temp;
  55. }
  56.  
  57.  
  58. /* Return the length of a string (not counting the null terminator). */
  59.  
  60. int CchSz(sz)
  61. CONST char *sz;
  62. {
  63.   int i;
  64.  
  65.   for (i = 0; *sz++; i++)
  66.     ;
  67.   return i;
  68. }
  69.  
  70.  
  71. /* Compare two strings. Return 0 if they are equal, a positive value if  */
  72. /* the first string is greater, and a negative if the second is greater. */
  73.  
  74. int NCompareSz(s1, s2)
  75. CONST char *s1, *s2;
  76. {
  77.   while (*s1 && *s1 == *s2)
  78.     s1++, s2++;
  79.   return *s1 - *s2;
  80. }
  81.  
  82.  
  83. /* Set a given number of bytes to zero given a starting pointer. */
  84.  
  85. void ClearB(pb, cb)
  86. lpbyte pb;
  87. int cb;
  88. {
  89.   while (cb-- > 0)
  90.     *pb++ = 0;
  91. }
  92.  
  93.  
  94. /* Copy a given number of bytes from one location to another. */
  95.  
  96. void CopyRgb(pbSrc, pbDst, cb)
  97. byte *pbSrc, *pbDst;
  98. int cb;
  99. {
  100.   while (cb-- > 0)
  101.     *pbDst++ = *pbSrc++;
  102. }
  103.  
  104.  
  105. /* Determine the sign of a number: -1 if value negative, +1 if value */
  106. /* positive, and 0 if it's zero.                                     */
  107.  
  108. real RSgn(r)
  109. real r;
  110. {
  111.   return r == 0.0 ? 0.0 : RSgn2(r);
  112. }
  113.  
  114.  
  115. /* Given an x and y coordinate, return the angle formed by a line from the */
  116. /* origin to this coordinate. This is just converting from rectangular to  */
  117. /* polar coordinates; however, we don't determine the radius here.         */
  118.  
  119. real Angle(x, y)
  120. real x, y;
  121. {
  122.   real a;
  123.  
  124.   if (x != 0.0) {
  125.     if (y != 0.0)
  126.       a = RAtn(y/x);
  127.     else
  128.       a = x < 0.0 ? rPi : 0.0;
  129.   } else
  130.     a = y < 0.0 ? -rPiHalf : rPiHalf;
  131.   if (a < 0.0)
  132.     a += rPi;
  133.   if (y < 0.0)
  134.     a += rPi;
  135.   return a;
  136. }
  137.  
  138.  
  139. /* Modulus function for floating point values, where we bring the given */
  140. /* parameter to within the range of 0 to 360.                           */
  141.  
  142. real Mod(d)
  143. real d;
  144. {
  145.   if (d >= rDegMax)        /* In most cases, our value is only slightly */
  146.     d -= rDegMax;          /* out of range, so we can test for it and   */
  147.   else if (d < 0.0)        /* avoid the more complicated arithmetic.    */
  148.     d += rDegMax;
  149.   if (d >= 0 && d < rDegMax)
  150.     return d;
  151.   return (d - RFloor(d/rDegMax)*rDegMax);
  152. }
  153.  
  154.  
  155. /* Another modulus function, this time for the range of 0 to 2 Pi. */
  156.  
  157. real ModRad(r)
  158. real r;
  159. {
  160.   while (r >= rPi2)    /* We assume our value is only slightly out of       */
  161.     r -= rPi2;         /* range, so test and never do any complicated math. */
  162.   while (r < 0.0)
  163.     r += rPi2;
  164.   return r;
  165. }
  166.  
  167.  
  168. /* Integer division - like the "/" operator but always rounds result down. */
  169.  
  170. long Dvd(x, y)
  171. long x, y;
  172. {
  173.   long z;
  174.  
  175.   if (y == 0)
  176.     return x;
  177.   z = x / y;
  178.   if (((x >= 0) == (y >= 0)) || x-z*y == 0)
  179.     return z;
  180.   return z - 1;
  181. }
  182.  
  183.  
  184. /*
  185. ******************************************************************************
  186. ** General Astrology Procedures.
  187. ******************************************************************************
  188. */
  189.  
  190. /* A similar modulus function: convert an integer to value from 1..12. */
  191.  
  192. int Mod12(i)
  193. int i;
  194. {
  195.   while (i > cSign)
  196.     i -= cSign;
  197.   while (i < 1)
  198.     i += cSign;
  199.   return i;
  200. }
  201.  
  202.  
  203. /* Convert an inputed fractional degrees/minutes value to a true decimal   */
  204. /* degree quantity. For example, the user enters the decimal value "10.30" */
  205. /* to mean 10 degrees and 30 minutes; this will return 10.5, i.e. 10       */
  206. /* degrees and 30 minutes expressed as a floating point degree value.      */
  207.  
  208. real DecToDeg(d)
  209. real d;
  210. {
  211.   return RSgn(d)*(RFloor(RAbs(d))+RFract(RAbs(d))*100.0/60.0);
  212. }
  213.  
  214.  
  215. /* This is the inverse of the above function. Given a true decimal value */
  216. /* for a zodiac degree, adjust it so the degrees are in the integer part */
  217. /* and the minute expressed as hundredths, e.g. 10.5 degrees -> 10.30    */
  218.  
  219. real DegToDec(d)
  220. real d;
  221. {
  222.   return RSgn(d)*(RFloor(RAbs(d))+RFract(RAbs(d))*60.0/100.0);
  223. }
  224.  
  225.  
  226. /* Return the shortest distance between two degrees in the zodiac. This is  */
  227. /* normally their difference, but we have to check if near the Aries point. */
  228.  
  229. real MinDistance(deg1, deg2)
  230. real deg1, deg2;
  231. {
  232.   real i;
  233.  
  234.   i = RAbs(deg1-deg2);
  235.   return i < rDegHalf ? i : rDegMax - i;
  236. }
  237.  
  238.  
  239. /* This is just like the above routine, except the min distance value  */
  240. /* returned will either be positive or negative based on whether the   */
  241. /* second value is ahead or behind the first one in a circular zodiac. */
  242.  
  243. real MinDifference(deg1, deg2)
  244. real deg1, deg2;
  245. {
  246.   real i;
  247.  
  248.   i = deg2 - deg1;
  249.   if (RAbs(i) < rDegHalf)
  250.     return i;
  251.   return RSgn(i)*(RAbs(i) - rDegMax);
  252. }
  253.  
  254.  
  255. /* Return the degree of the midpoint between two zodiac positions, making */
  256. /* sure we return the true midpoint closest to the positions in question. */
  257.  
  258. real Midpoint(deg1, deg2)
  259. real deg1, deg2;
  260. {
  261.   real mid;
  262.  
  263.   mid = (deg1+deg2)/2.0;
  264.   return MinDistance(deg1, mid) < rDegQuad ? mid : Mod(mid+rDegHalf);
  265. }
  266.  
  267.  
  268. /* Given a planet and sign, determine whether: The planet rules the sign, */
  269. /* the planet has its fall in the sign, the planet exalts in the sign, or */
  270. /* is debilitated in the sign; and return an appropriate character.       */
  271.  
  272. char Dignify(obj, sign)
  273. int obj, sign;
  274. {
  275.   if (obj > oNorm)
  276.     return ' ';
  277.   if (ruler1[obj] == sign || ruler2[obj] == sign)
  278.     return 'R';
  279.   if (ruler1[obj] == Mod12(sign+6) || ruler2[obj] == Mod12(sign+6))
  280.     return 'F';
  281.   if (exalt[obj] == sign)
  282.     return 'e';
  283.   if (exalt[obj] == Mod12(sign+6))
  284.     return 'd';
  285.   return '-';
  286. }
  287.  
  288.  
  289. /* Determine the number of days in a particular month. The year is needed, */
  290. /* too, because we have to check for leap years in the case of February.   */
  291.  
  292. int DayInMonth(month, year)
  293. int month, year;
  294. {
  295.   int d;
  296.  
  297.   if (month == mSep || month == mApr || month == mJun || month == mNov)
  298.     d = 30;
  299.   else if (month != mFeb)
  300.     d = 31;
  301.   else {
  302.     d = 28;
  303.     if (year % 4 == 0 &&
  304.       (year % 100 != 0 || year % 400 == 0 || year <= yeaJ2G))
  305.       d++;
  306.   }
  307.   return d;
  308. }
  309.  
  310.  
  311. /* Return the actual number of days in a particular month. Normally, this  */
  312. /* is the same as the above routine which determines the index of the last */
  313. /* day of the month, but the values can differ when changing between       */
  314. /* calendar systems (Julian to Gregorian) in which one can jump over days. */
  315.  
  316. int DaysInMonth(month, year)
  317. int month, year;
  318. {
  319.   int d;
  320.  
  321.   d = DayInMonth(month, year);
  322.   if (year == yeaJ2G && month == monJ2G)
  323.     d -= (dayJ2G2 - dayJ2G1 - 1);
  324.   return d;
  325. }
  326.  
  327.  
  328. /* Return the day of the week (Sunday is 0) of the specified given date. */
  329.  
  330. int DayOfWeek(month, day, year)
  331. int month, day, year;
  332. {
  333.   int d;
  334.  
  335.   d = (int)((MdyToJulian(month, day, year) + 1) % 7);
  336.   return d < 0 ? d+7 : d;
  337. }
  338.  
  339.  
  340. /* Given a day, and the month and year it falls in, add a number of days    */
  341. /* to it and return the new day index. As month changes are not checked for */
  342. /* here, this is mostly just adding the offset to the day; however we need  */
  343. /* to check for calendar changes for when days in a month may be skipped.   */
  344.  
  345. int AddDay(month, day, year, delta)
  346. int month, day, year, delta;
  347. {
  348.   int d;
  349.  
  350.   d = day + delta;
  351.   if (year == yeaJ2G && month == monJ2G) {     /* Check for Julian to  */
  352.     if (d > dayJ2G1 && d < dayJ2G2)            /* Gregorian crossover. */
  353.       d += NSgn(delta)*(dayJ2G2-dayJ2G1-1);
  354.   }
  355.   return d;
  356. }
  357.  
  358.  
  359. /* Given an aspect and two objects making that aspect with each other,   */
  360. /* return the maximum orb allowed for such an aspect. Normally this only */
  361. /* depends on the aspect itself, but some objects require narrow orbs,   */
  362. /* and some allow wider orbs, so check for these cases.                  */
  363.  
  364. real GetOrb(obj1, obj2, asp)
  365. int obj1, obj2, asp;
  366. {
  367.   real orb, r;
  368.  
  369.   orb = rAspOrb[asp];
  370.   r = obj1 > oNorm ? 2.0 : rObjOrb[obj1];
  371.   orb = Min(orb, r);
  372.   r = obj2 > oNorm ? 2.0 : rObjOrb[obj2];
  373.   orb = Min(orb, r);
  374.   if (obj1 <= oNorm)
  375.     orb += rObjAdd[obj1];
  376.   if (obj2 <= oNorm)
  377.     orb += rObjAdd[obj2];
  378.   return orb;
  379. }
  380.  
  381.  
  382. /*
  383. ******************************************************************************
  384. ** String Procedures.
  385. ******************************************************************************
  386. */
  387.  
  388. /* Exit the program, and do any cleanup necessary. Note that if we had     */
  389. /* a non-fatal error, and we are in the -Q loop mode, then we won't        */
  390. /* actually terminate the program, but drop back to the command line loop. */
  391.  
  392. void Terminate(tc)
  393. int tc;
  394. {
  395.   char sz[cchSzDef];
  396.  
  397.   if (us.fNoQuit)
  398.     return;
  399.   if (tc == tcForce) {
  400.     is.S = stdout;
  401.     AnsiColor(kWhite);
  402.     sprintf(sz, "\n%s %s exited.\n", szAppName, szVersionCore);
  403.     PrintSz(sz);
  404.   }
  405.   if (tc == tcError && us.fLoop)
  406.     return;
  407.   if (us.fAnsiColor) {
  408.     sprintf(sz, "%c[0m", chEscape);    /* Get out of any Ansi color mode. */
  409.     PrintSz(sz);
  410.   }
  411.   exit(abs(tc));
  412. }
  413.  
  414.  
  415. /* Print a string on the screen. A seemingly simple operation, however we */
  416. /* keep track of what column we are printing at after each newline so we  */
  417. /* can automatically clip at the appropriate point, and we keep track of  */
  418. /* the row we are printing at, so we may prompt before screen scrolling.  */
  419.  
  420. void PrintSz(sz)
  421. CONST char *sz;
  422. {
  423.   char szInput[cchSzDef], *pch;
  424. #ifndef WIN
  425.   int fT;
  426. #endif
  427.  
  428.   for (pch = (char *)sz; *pch; pch++) {
  429.     if (*pch != '\n') {
  430.       is.cchCol++;
  431.       if (us.fClip80 && is.cchCol >= us.nScreenWidth)  /* Clip if need be. */
  432.         continue;
  433.     } else {
  434.       is.cchRow++;
  435.       is.cchCol = 0;
  436.     }
  437. #ifdef WIN
  438.     if (is.S == stdout) {
  439.       if ((byte)*pch >= ' ') {
  440.         szInput[0] = *pch; szInput[1] = chNull;
  441.         TextOut(wi.hdc, (is.cchCol - 1 - wi.xScroll * 10) * wi.xChar + 4,
  442.           (is.cchRow - wi.yScroll * 10) * wi.yChar, szInput, 1);
  443.       }
  444.     } else
  445. #endif
  446.     putc(*pch, is.S);
  447. #ifndef WIN
  448.     if (*pch == '\n' && is.S == stdout &&
  449.       us.nScrollRow > 0 && is.cchRow >= us.nScrollRow) {
  450.  
  451.       /* If we've printed 'n' rows, stop and wait for a line to be entered. */
  452.  
  453.       fT = us.fAnsiColor;
  454.       us.fAnsiColor = fFalse;
  455.       InputString("Press return to continue scrolling", szInput);
  456.       us.fAnsiColor = fT;
  457.       is.cchRow = 0;
  458.  
  459.       /* One can actually give a few simple commands before hitting return. */
  460.  
  461.       if (szInput[0] == '.' || szInput[0] == 'q')
  462.         Terminate(tcForce);
  463.       else if (szInput[0] == '8')
  464.         not(us.fClip80);
  465.       else if (szInput[0] == 'Q')
  466.         us.nScrollRow = 0;
  467.       else if (szInput[0] == 'k') {
  468.         if (us.fAnsiColor)
  469.           AnsiColor(kDefault);
  470.         not(us.fAnsiColor); not(us.fAnsiChar);
  471.       }
  472.     }
  473. #else
  474.     if (*pch == '\n' && is.S == stdout && wi.hdcPrint != hdcNil &&
  475.       is.cchRow >= us.nScrollRow) {
  476.  
  477.       /* If writing to the printer, start a new page when appropriate. */
  478.  
  479.       is.cchRow = 0;
  480.       EndPage(wi.hdcPrint);
  481.       StartPage(wi.hdcPrint);
  482.       /* StartPage clobbers all the DC settings */
  483.       SetMapMode(wi.hdcPrint, MM_ANISOTROPIC);      /* For SetViewPortExt */
  484.       SetViewportOrg(wi.hdcPrint, 0, 0);
  485.       SetViewportExt(wi.hdcPrint, GetDeviceCaps(wi.hdcPrint, HORZRES),
  486.         GetDeviceCaps(wi.hdcPrint, VERTRES));
  487.       SetWindowOrg(wi.hdcPrint, 0, 0);
  488.       SetWindowExt(wi.hdcPrint, wi.xClient, wi.yClient);
  489.       SetBkMode(wi.hdcPrint, TRANSPARENT);
  490.       SelectObject(wi.hdcPrint, wi.hfont);
  491.     }
  492. #endif
  493.   }
  494. }
  495.  
  496.  
  497. /* Print a single character on the screen. */
  498.  
  499. void PrintCh(ch)
  500. char ch;
  501. {
  502.   char sz[2];
  503.  
  504.   sz[0] = ch; sz[1] = chNull;    /* Treat char as a string of length one. */
  505.   PrintSz(sz);                   /* Then call above to print the string.  */
  506. }
  507.  
  508.  
  509. /* Print a string on the screen. Unlike the normal PrintSz(), here we still */
  510. /* go to the standard output even if text is being sent to a file with -os. */
  511.  
  512. void PrintSzScreen(sz)
  513. char *sz;
  514. {
  515.   FILE *fileT;
  516.  
  517.   fileT = is.S;
  518.   is.S = stdout;
  519.   PrintSz(sz);
  520.   is.S = fileT;
  521. }
  522.  
  523.  
  524. /* Print a general user message given a string. This is just like the */
  525. /* warning displayer below just that we print in a different color.   */
  526.  
  527. void PrintNotice(sz)
  528. char *sz;
  529. {
  530. #ifndef WIN
  531.   /* Notice messages are ignored in the Windows version. */
  532.   AnsiColor(kYellow);
  533.   fprintf(stderr, "%s\n", sz);
  534.   AnsiColor(kDefault);
  535. #endif
  536. }
  537.  
  538.  
  539. /* Print a warning message given a string. This is called in non-fatal  */
  540. /* cases where we return to normal execution after printing the string. */
  541.  
  542. void PrintWarning(sz)
  543. char *sz;
  544. {
  545. #ifndef WIN
  546.   AnsiColor(kRed);
  547.   fprintf(stderr, "%s\n", sz);
  548.   AnsiColor(kDefault);
  549. #else
  550.   char szT[cchSzDef];
  551.  
  552.   sprintf(szT, "%s Warning", szAppName);
  553.   MessageBox((HWND)NULL, sz, szT, MB_ICONSTOP);
  554. #endif
  555. }
  556.  
  557.  
  558. /* Print an error message. This is called in more serious cases which halt */
  559. /* running of the current chart sequence, which can terminate the program  */
  560. /* but isn't a fatal error in that we can still fall back to the -Q loop.  */
  561.  
  562. void PrintError(sz)
  563. char *sz;
  564. {
  565. #ifndef WIN
  566.   AnsiColor(kRed);
  567.   fprintf(stderr, "%s: %s\n", szAppName, sz);
  568.   Terminate(tcError);
  569.   AnsiColor(kDefault);
  570. #else
  571.   char szT[cchSzDef];
  572.  
  573.   sprintf(szT, "%s Error", szAppName);
  574.   MessageBox((HWND)NULL, sz, szT, MB_ICONEXCLAMATION);
  575. #endif
  576. }
  577.  
  578.  
  579. /* Simplification for a commonly printed error message. */
  580.  
  581. void ErrorArgc(szOpt)
  582. char *szOpt;
  583. {
  584.   char sz[cchSzDef];
  585.  
  586.   sprintf(sz, "Too few options to switch %c%s", chSwitch, szOpt);
  587.   PrintError(sz);
  588. }
  589.  
  590.  
  591. /* Another simplification for a commonly printed error message. */
  592.  
  593. void ErrorValN(szOpt, nVal)
  594. char *szOpt;
  595. int nVal;
  596. {
  597.   char sz[cchSzDef];
  598.  
  599.   sprintf(sz, "Value %d passed to switch %c%s out of range.\n",
  600.     nVal, chSwitch, szOpt);
  601.   PrintError(sz);
  602. }
  603.  
  604.  
  605. /* Yet another place to print a type of error message. */
  606.  
  607. void ErrorArgv(szOpt)
  608. char *szOpt;
  609. {
  610.   char sz[cchSzDef];
  611.  
  612.   sprintf(sz, "The switch %c%s is not allowed now.\n", chSwitch, szOpt);
  613.   PrintError(sz);
  614. }
  615.  
  616.  
  617. /* Still another place to print a type of error message. */
  618.  
  619. void ErrorSwitch(szOpt)
  620. char *szOpt;
  621. {
  622.   char sz[cchSzDef];
  623.  
  624.   sprintf(sz, "Unknown switch '%s'", szOpt);
  625.   PrintError(sz);
  626. }
  627.  
  628.  
  629. #ifdef PLACALC
  630. /* Print error messages dealing with ephemeris file access. */
  631.  
  632. void ErrorEphem(sz, l)
  633. char *sz;
  634. long l;
  635. {
  636.   char szT[cchSzDef];
  637.  
  638.   if (l < 0)
  639.     sprintf(szT, "Ephemeris file %s not found.\n", sz);
  640.   else
  641.     sprintf(szT, "Seek error in file %s at position %ld.\n", sz, l);
  642.   is.fNoEphFile = fTrue;
  643.   PrintWarning(szT);
  644. }
  645. #endif
  646.  
  647.  
  648. /* A simple procedure used throughout Astrolog: Print a particular */
  649. /* character on the screen 'n' times.                              */
  650.  
  651. void PrintTab(ch, cch)
  652. char ch;
  653. int cch;
  654. {
  655.   int i;
  656.  
  657.   for (i = 0; i < cch; i++)
  658.     PrintCh(ch);
  659. }
  660.  
  661.  
  662. /* Set an Ansi or MS Windows text color. */
  663.  
  664. void AnsiColor(k)
  665. int k;
  666. {
  667.   char sz[cchSzDef];
  668.   int cchSav;
  669.  
  670. #ifdef WIN
  671.   if (is.S == stdout) {
  672.     if (k < 0)
  673.       k = kLtGray;
  674.     SetTextColor(wi.hdc,
  675.       (COLORREF)rgbbmp[us.fAnsiColor ? k : (gs.fInverse ? kBlack : kLtGray)]);
  676.     return;
  677.   }
  678. #endif
  679.  
  680.   /* Special case: If we are passed the value Reverse, and Ansi color is */
  681.   /* not only on but set to a value > 1, then enter reverse video mode.  */
  682.  
  683.   if (!us.fAnsiColor || (k == kReverse && us.fAnsiColor < 2))
  684.     return;
  685.   cchSav = is.cchCol;
  686.   is.cchCol = 0;
  687.   sprintf(sz, "%c[", chEscape);
  688.   PrintSz(sz);
  689.   if (k == kDefault)
  690.     PrintCh('0');
  691.   else if (k == kReverse) {
  692.     PrintCh('7');
  693.   } else {
  694.     sprintf(sz, "%c;%d", k > 7 ? '1' : '0', 30 + (k & 7));
  695.     PrintSz(sz);
  696.   }
  697.   PrintCh('m');
  698.   is.cchCol = cchSav;
  699. }
  700.  
  701.  
  702. /* Print a zodiac position on the screen. This basically just prints the */
  703. /* string returned from SzZodiac() below, except we take care of color.  */
  704.  
  705. void PrintZodiac(deg)
  706. real deg;
  707. {
  708.   AnsiColor(kElemA[(int)(deg / 30.0) & 3]);
  709.   PrintSz(SzZodiac(deg));
  710.   AnsiColor(kDefault);
  711. }
  712.  
  713.  
  714. /* Given a zodiac position, return a string containing it as it's */
  715. /* formatted for display to the user.                             */
  716.  
  717. char *SzZodiac(deg)
  718. real deg;
  719. {
  720.   static char szZod[11];
  721.   int sign, d, m;
  722.   real s;
  723.  
  724.   switch (us.nDegForm) {
  725.   case 0:
  726.  
  727.     /* Normally, we format the position in degrees/sign/minutes format: */
  728.  
  729.     deg = Mod(deg + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0));
  730.     sign = (int)deg / 30;
  731.     d = (int)deg - sign*30;
  732.     m = (int)(RFract(deg)*60.0);
  733.     sprintf(szZod, "%2d%c%c%c%02d", d, chSig3(sign + 1), m);
  734.     if (is.fSeconds) {
  735.       s = RFract(deg)*60.0; s = RFract(s)*60.0;
  736.       sprintf(&szZod[7], "'%02d\"", (int)s);
  737.     }
  738.     break;
  739.  
  740.   case 1:
  741.     /* However, if -sh switch in effect, get position in hours/minutes: */
  742.  
  743.     deg = Mod(deg + (is.fSeconds ? rRound/4.0/60.0 : rRound/4.0));
  744.     d = (int)deg / 15;
  745.     m = (int)((deg - (real)d*15.0)*4.0);
  746.     sprintf(szZod, "%2dh,%02dm", d, m);
  747.     if (is.fSeconds) {
  748.       s = RFract(deg)*4.0; s = RFract(s)*60.0;
  749.       sprintf(&szZod[7], ",%02ds", (int)s);
  750.     }
  751.     break;
  752.  
  753.   default:
  754.     /* Otherwise, if -sd in effect, format position as a simple degree: */
  755.  
  756.     sprintf(szZod, is.fSeconds ? "%11.7f" : "%7.3f", deg);
  757.     break;
  758.   }
  759.   return szZod;
  760. }
  761.  
  762.  
  763. /* This is similar to formatting a zodiac degree, but here we return a */
  764. /* string of a (signed) declination value in degrees and minutes.      */
  765.  
  766. char *SzAltitude(deg)
  767. real deg;
  768. {
  769.   static char szAlt[10];
  770.   int d, m, f;
  771.   real s;
  772.   char ch;
  773.  
  774.   f = deg < 0.0;
  775.   deg = RAbs(deg) + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0);
  776.   d = (int)deg;
  777.   m = (int)(RFract(deg)*60.0);
  778.   ch = us.fAnsiChar > 1 ? 128 : chDeg1;
  779.   sprintf(szAlt, "%c%2d%c%02d'", f ? '-' : '+', d, ch, m);
  780.   if (is.fSeconds) {
  781.     s = RFract(deg)*60.0; s = RFract(s)*60.0;
  782.     sprintf(&szAlt[7], "%02d\"", (int)s);
  783.   }
  784.   return szAlt;
  785. }
  786.  
  787.  
  788. /* Here we return a string simply expressing the given value as degrees */
  789. /* and minutes (and sometimes seconds) in the 0 to 360 degree circle.   */
  790.  
  791. char *SzDegree(deg)
  792. real deg;
  793. {
  794.   static char szPos[11];
  795.   int d, m;
  796.   real s;
  797.  
  798.   deg = RAbs(deg) + (is.fSeconds ? rRound/60.0/60.0 : rRound/60.0);
  799.   d = (int)deg;
  800.   m = (int)(RFract(deg)*60.0);
  801.   sprintf(szPos, "%3d%c%02d'", d, chDeg1, m);
  802.   if (is.fSeconds) {
  803.     s = RFract(deg)*60.0; s = RFract(s)*60.0;
  804.     sprintf(&szPos[7], "%02d\"", (int)s);
  805.   }
  806.   return szPos;
  807. }
  808.  
  809.  
  810. /* Another string formatter, here we return a date string given a month,    */
  811. /* day, and year. We format with the day or month first based on whether    */
  812. /* the "European" date variable is set or not. The routine also takes a     */
  813. /* parameter to indicate how much the string should be abbreviated, if any. */
  814.  
  815. char *SzDate(mon, day, yea, nFormat)
  816. int mon, day, yea, nFormat;
  817. {
  818.   static char szDat[20];
  819.  
  820.   if (us.fEuroDate) {
  821.     switch (nFormat) {
  822.     case  2: sprintf(szDat, "%2d %c%c%c%5d", day, chMon3(mon), yea); break;
  823.     case  1: sprintf(szDat, "%d %s %d", day, szMonth[mon], yea);     break;
  824.     case -1: sprintf(szDat, "%2d-%2d-%2d", day, mon, abs(yea)%100);  break;
  825.     default: sprintf(szDat, "%2d-%2d-%4d", day, mon, yea);           break;
  826.     }
  827.   } else {
  828.     switch (nFormat) {
  829.     case  3: sprintf(szDat, "%c%c%c %2d, %d", chMon3(mon), day, yea); break;
  830.     case  2: sprintf(szDat, "%c%c%c %2d%5d", chMon3(mon), day, yea);  break;
  831.     case  1: sprintf(szDat, "%s %d, %d", szMonth[mon], day, yea);     break;
  832.     case -1: sprintf(szDat, "%2d/%2d/%2d", mon, day, abs(yea)%100);   break;
  833.     default: sprintf(szDat, "%2d/%2d/%4d", mon, day, yea);            break;
  834.     }
  835.   }
  836.   return szDat;
  837. }
  838.  
  839.  
  840. /* Return a string containing the given time expressed as an hour and */
  841. /* minute quantity. This is formatted in 24 hour or am/pm time based  */
  842. /* on whether the "European" time format flag is set or not.          */
  843.  
  844. char *SzTime(hr, min)
  845. int hr, min;
  846. {
  847.   static char szTim[8];
  848.  
  849.   while (min >= 60) {
  850.     min -= 60;
  851.     hr++;
  852.   }
  853.   while (hr < 0)
  854.     hr += 24;
  855.   while (hr >= 24)
  856.     hr -= 24;
  857.   if (us.fEuroTime)
  858.     sprintf(szTim, "%2d:%02d", hr, min);
  859.   else
  860.     sprintf(szTim, "%2d:%02d%cm", Mod12(hr), min, hr < 12 ? 'a' : 'p');
  861.   return szTim;
  862. }
  863.  
  864.  
  865. /* This just determines the correct hour and minute and calls the above. */
  866.  
  867. char *SzTim(tim)
  868. real tim;
  869. {
  870.   return SzTime(NFloor(tim), (int)(RFract(RAbs(tim))*100.0+rRound/600.0));
  871. }
  872.  
  873.  
  874. /* Return a string containing the given time zone, given as a real value     */
  875. /* having the hours before GMT in the integer part and minutes fractionally. */
  876.  
  877. char *SzZone(zon)
  878. real zon;
  879. {
  880.   static char szZon[7];
  881.  
  882.   sprintf(szZon, "%c%d:%02d", zon > 0.0 ? '-' : '+', (int)RAbs(zon),
  883.     (int)(RFract(RAbs(zon))*100.0+rRound/60.0));
  884.   return szZon;
  885. }
  886.  
  887.  
  888. /* Nicely format the given longitude and latitude locations and return    */
  889. /* them in a string. Various parts of the program display a chart header, */
  890. /* and this allows the similar computations to be coded only once.        */
  891.  
  892. char *SzLocation(lon, lat)
  893. real lon, lat;
  894. {
  895.   static char szLoc[15];
  896.   int i, j;
  897.   char ch;
  898.  
  899.   i = (int)(RFract(RAbs(lon))*100.0+rRound);
  900.   j = (int)(RFract(RAbs(lat))*100.0+rRound);
  901.   ch = us.fAnsiChar > 1 ? 128 : chDeg1;
  902.   if (us.fAnsiChar != 3) {
  903.     sprintf(szLoc, "%3.0f%c%02d%c%3.0f%c%02d%c",
  904.       RFloor(RAbs(lon)), ch, i, lon < 0.0 ? 'E' : 'W',
  905.       RFloor(RAbs(lat)), ch, j, lat < 0.0 ? 'S' : 'N');
  906.   } else {
  907.     sprintf(szLoc, "%3.0f%c%02d%3.0f%c%02d",
  908.       RFloor(RAbs(lon)), lon < 0.0 ? 'E' : 'W', i,
  909.       RFloor(RAbs(lat)), lat < 0.0 ? 'S' : 'N', j);
  910.   }
  911.   return szLoc;
  912. }
  913.  
  914.  
  915. #ifdef TIME
  916. /* Compute the date and time it is right now as the program is running      */
  917. /* using the computer's internal clock. We do this by getting the number    */
  918. /* of seconds which have passed since January 1, 1970 and going from there. */
  919. /* The time return value filled is expressed in the given zone parameter.   */
  920.  
  921. void GetTimeNow(mon, day, yea, tim, zon)
  922. int *mon, *day, *yea;
  923. real *tim, zon;
  924. {
  925.   dword curtimer;
  926.   int min, sec;
  927.   real hr;
  928.  
  929.   time(&curtimer);
  930.   sec = (int)(curtimer % 60);
  931.   curtimer = curtimer / 60 + us.lTimeAddition;
  932.   min = (int)(curtimer % 60);
  933.   curtimer /= 60;
  934. #ifdef MAC
  935.   curtimer += 8;
  936. #endif
  937.   hr = (real)(curtimer % 24) - zon;
  938.   curtimer /= 24;
  939.   while (hr < 0.0) {
  940.     curtimer--;
  941.     hr += 24.0;
  942.   }
  943.   while (hr >= 24.0) {
  944.     curtimer++;
  945.     hr -= 24.0;
  946.   }
  947.   curtimer += ldTime;  /* Number of days between 1/1/1970 and 1/1/4713 BC. */
  948.   JulianToMdy((real)curtimer, mon, day, yea);
  949.   *tim = hr + (real)min / 100.0 + (real)sec / 6000.0;
  950. }
  951. #endif /* TIME */
  952.  
  953.  
  954. #ifdef PCG
  955. /* Map one character value to another. This is used in processing special  */
  956. /* keys and alt key combinations, which are read in from the keyboard as a */
  957. /* zero immediately followed by some value. This converts that value into  */
  958. /* something more useful to process and deal with.                         */
  959.  
  960. int NFromAltN(nAlt)
  961. int nAlt;
  962. {
  963.   /* Map number pad keys to the numbers characters they correspond to. */
  964.   if (nAlt == 82)
  965.     return '0';
  966.   else if (FBetween(nAlt, 79, 81))
  967.     return '1' + nAlt - 79;
  968.   else if (FBetween(nAlt, 75, 77))
  969.     return '4' + nAlt - 75;
  970.   else if (FBetween(nAlt, 71, 73))
  971.     return '7' + nAlt - 71;
  972.  
  973.   /* Map F1 through F12 function keys to the values 201-212. */
  974.   else if (FBetween(nAlt, 59, 68))
  975.     return 201 + nAlt - 59;
  976.   else if (FBetween(nAlt, 133, 134))
  977.     return 211 + nAlt - 133;
  978.  
  979.   /* Map Shift+F1 through Shift+F12 keys to the values 213-224. */
  980.   else if (FBetween(nAlt, 84, 93))
  981.     return 213 + nAlt - 84;
  982.   else if (FBetween(nAlt, 135, 136))
  983.     return 223 + nAlt - 135;
  984.  
  985.   /* Map Control+F1 through Control+F12 keys to the values 225-236. */
  986.   else if (FBetween(nAlt, 94, 103))
  987.     return 225 + nAlt - 94;
  988.   else if (FBetween(nAlt, 137, 138))
  989.     return 235 + nAlt - 137;
  990.  
  991.   /* Map Alt+F1 through Alt+F12 keys to the values 237-248. */
  992.   else if (FBetween(nAlt, 104, 113))
  993.     return 237 + nAlt - 104;
  994.   else if (FBetween(nAlt, 139, 140))
  995.     return 247 + nAlt - 139;
  996.  
  997.   return chNull;
  998. }
  999. #endif
  1000.  
  1001.  
  1002. /* Given a string representing the complete pathname to a file, strip off    */
  1003. /* all the path information leaving just the filename itself. This is called */
  1004. /* by the main program to determine the name of the Astrolog executable.     */
  1005.  
  1006. char *ProcessProgname(szPath)
  1007. char *szPath;
  1008. {
  1009.   char *b, *c, *e;
  1010.  
  1011.   b = c = szPath;
  1012.   while (*c) {
  1013. #ifdef PC
  1014.     *c = ChUncap(*c);    /* Because DOS filenames are case insensitive. */
  1015. #endif
  1016.     c++;
  1017.   }
  1018.   e = c;
  1019.   while (c > b && *c != '.')
  1020.     c--;
  1021.   if (c > b)
  1022.     *c = 0;
  1023.   else
  1024.     c = e;
  1025.   while (c > b && *c != chDirSep)
  1026.     c--;
  1027.   if (c > b)
  1028.     szPath = c+1;
  1029.   return szPath;
  1030. }
  1031.  
  1032.  
  1033. /* Given a string, return a pointer to a persistent version of it, where  */
  1034. /* 'persistent' means its contents won't be invalidated when the stack    */
  1035. /* frame changes. Strings such as macros, et al, need to be in their own  */
  1036. /* space and can't just be local variables in a function reading them in. */
  1037.  
  1038. char *SzPersist(szSrc)
  1039. char *szSrc;
  1040. {
  1041.   char szT[cchSzDef], *szNew;
  1042.   int cb;
  1043.  
  1044.   /* Some strings such as outer level command line parameter arguments */
  1045.   /* already persist, so we can just return the same string passed in. */
  1046.   if (is.fSzPersist)
  1047.     return szSrc;
  1048.  
  1049.   /* Otherwise we make a copy of the string in the local heap and use it. */
  1050.   cb = CchSz(szSrc)+1;
  1051.   AllocateNear(szNew, cb);
  1052.   if (szNew == NULL) {
  1053.     sprintf(szT, "%s: Not enough near memory for string (%d bytes).",
  1054.       szAppName, cb);
  1055.     PrintWarning(szT);
  1056.   } else
  1057.     CopyRgb((byte *)szSrc, (byte *)szNew, cb);
  1058.   return szNew;
  1059. }
  1060.  
  1061.  
  1062. /* This is Astrolog's memory allocation routine, returning a pointer given */
  1063. /* a size, a flag for if it is a more than 64K huge allocation, and a      */
  1064. /* string to use when printing an error if the allocation fails.           */
  1065.  
  1066. lpbyte PAllocate(lcb, fHuge, szType)
  1067. long lcb;
  1068. bool fHuge;
  1069. char *szType;
  1070. {
  1071.   char szT[cchSzDef];
  1072.   lpbyte lp;
  1073.  
  1074.   if (fHuge)
  1075.     AllocateHuge(lp, lcb);
  1076.   else
  1077.     AllocateFar(lp, (int)lcb);
  1078. #ifdef PC
  1079.   /* For PC's the array better not cross a segment boundary. */
  1080.   if (lp && !fHuge && WHi(WLo(lp) + lcb) > 0)
  1081.     lp = NULL;
  1082. #endif
  1083.   if (lp == NULL && szType) {
  1084.     sprintf(szT, "%s: Not enough memory for %s (%ld bytes).",
  1085.       szAppName, szType, lcb);
  1086.     PrintWarning(szT);
  1087.   }
  1088.   return lp;
  1089. }
  1090.  
  1091. /* general.c */
  1092.